package org.hackystat.sdsa.analyzer.control;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.xml.datatype.XMLGregorianCalendar;

import org.hackystat.sdsa.analyzer.model.activity.UnitTestActivity;
import org.hackystat.sdsa.analyzer.model.activity.edit.EditActivity;
import org.hackystat.sdsa.analyzer.model.activity.edit.ProductionEditActivity;
import org.hackystat.sdsa.analyzer.model.activity.edit.UnitTestEditActivity;
import org.hackystat.sdsa.service.resource.devstream.jaxb.DevStream;
import org.hackystat.sdsa.service.resource.devstream.jaxb.EpisodeTO;
import org.hackystat.sensorbase.resource.sensordata.jaxb.Properties;
import org.hackystat.sensorbase.resource.sensordata.jaxb.Property;
import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData;
import org.hackystat.utilities.tstamp.Tstamp;
import org.junit.Test;

/**
 * Test functionality of the DevStream processor.
 * 
 * @author hongbing
 */
public class TestDevStreamBuilder {
  /** Production resource. */
  private String prodResource = "file:/C:/cvs/work/com/foo/Bar.java";
  /** Test resource. */
  private String testResource = "file:/C:/cvs/work/com/foo/TestBar.java";
  /** Logger. */
  private Logger logger = Logger.getLogger(TestDevStreamBuilder.class.getName());

  
  /**
   * Make a production edit sensor data.
   * 
   * @param tstamp Time stamp.
   * @param byteSize Byte size of the product code. 
   * @param statements Number of statements.
   * @param methods Number of methods.
   * @return SensorData instance.
   * @throws Exception If error occurs.
   */
  private SensorData makeProdEdit (XMLGregorianCalendar tstamp, String byteSize, 
      String statements, String methods) throws Exception {
    SensorData sensorData = new SensorData();
    sensorData.setTimestamp(tstamp);
    sensorData.setResource(prodResource);
    sensorData.setSensorDataType("DevEvent");

    Properties props = new Properties();
    sensorData.setProperties(props);
    
    Property type = new Property();
    type.setKey("Type");
    type.setValue("Edit");
    props.getProperty().add(type);
    
    Property subType = new Property();
    subType.setKey("Subtype");
    subType.setValue("StateChange");
    props.getProperty().add(subType);
    
    Property currentSize = new Property();
    currentSize.setKey(EditActivity.CURRENT_SIZE);
    currentSize.setValue(byteSize);
    props.getProperty().add(currentSize);

    Property className = new Property();
    className.setKey(ProductionEditActivity.CLASS_NAME);
    className.setValue("com.foo.Bar");
    props.getProperty().add(className);

    Property currentStatements = new Property();
    currentStatements.setKey(ProductionEditActivity.CURRENT_STATEMENTS);
    currentStatements.setValue(statements);
    props.getProperty().add(currentStatements);

    Property currentMethods = new Property();
    currentMethods.setKey(ProductionEditActivity.CURRENT_METHODS);
    currentMethods.setValue(methods);
    props.getProperty().add(currentMethods);

    return sensorData;
  }
  
  /**
   * Makes test edit sensor data. 
   * 
   * @param tstamp Time stamp.
   * @param byteSize Byte size.
   * @param statements Number of statements.
   * @param methods Number of methods.
   * @param testMethods Number of test methods.
   * @param testAssertions Number of test assertions.
   * 
   * @return Unit test edit sensor data.
   * @throws Exception If error occurs.
   */
  private SensorData makeUnitTestEdit(XMLGregorianCalendar tstamp, String byteSize, 
      String statements, String methods, String testMethods, String testAssertions) 
        throws Exception {
    SensorData sensorData = new SensorData();
    sensorData.setTimestamp(tstamp);
    sensorData.setResource(this.testResource);
    sensorData.setSensorDataType("DevEvent");

    Properties props = new Properties();
    sensorData.setProperties(props);
    
    Property type = new Property();
    type.setKey("Type");
    type.setValue("Edit");
    props.getProperty().add(type);
    
    Property subType = new Property();
    subType.setKey("Subtype");
    subType.setValue("StateChange");
    props.getProperty().add(subType);
    
    Property currentSize = new Property();
    currentSize.setKey(EditActivity.CURRENT_SIZE);
    currentSize.setValue(byteSize);
    props.getProperty().add(currentSize);

    Property className = new Property();
    className.setKey(ProductionEditActivity.CLASS_NAME);
    className.setValue("com.foo.TestBar");
    props.getProperty().add(className);

    Property currentStatements = new Property();
    currentStatements.setKey(ProductionEditActivity.CURRENT_STATEMENTS);
    currentStatements.setValue(statements);
    props.getProperty().add(currentStatements);

    Property currentMethods = new Property();
    currentMethods.setKey(ProductionEditActivity.CURRENT_METHODS);
    currentMethods.setValue(methods);
    props.getProperty().add(currentMethods);

    Property currentTestMethods = new Property();
    currentTestMethods.setKey(UnitTestEditActivity.CURRENT_TEST_METHODS);
    currentTestMethods.setValue(testMethods);
    props.getProperty().add(currentTestMethods);

    Property currentTestAssertions = new Property();
    currentTestAssertions.setKey(UnitTestEditActivity.CURRENT_TEST_ASSERTIONS);
    currentTestAssertions.setValue(testAssertions);
    props.getProperty().add(currentTestAssertions);
    
    return sensorData;
  }

  /**
   * Makes unit test activity. 
   * 
   * @param tstamp Time stamp.
   * @param failureMessage Failure message.
   * @return Unit test sensor data.
   * @throws Exception If error occurs.
   */
  private SensorData makeUnitTest(XMLGregorianCalendar tstamp, String failureMessage) 
      throws Exception {
    SensorData sensorData = new SensorData();
    sensorData.setTimestamp(tstamp);
    sensorData.setResource(this.testResource);
    sensorData.setSensorDataType("DevEvent");

    sensorData.setRuntime(tstamp);

    // Add property
    Properties props = new Properties();
    sensorData.setProperties(props);

    Property type = new Property();
    type.setKey("Type");
    type.setValue("Test");
    props.getProperty().add(type);
    
    Property failureString = new Property();
    failureString.setKey(UnitTestActivity.FAILURE_STRING);
    failureString.setValue(failureMessage);
    props.getProperty().add(failureString);

    Property elapsedTime = new Property();
    elapsedTime.setKey(UnitTestActivity.ELAPSED_TIME);
    elapsedTime.setValue("12");
    props.getProperty().add(elapsedTime);

    Property testName = new Property();
    testName.setKey(UnitTestActivity.TEST_NAME);
    testName.setValue("testSingle()");
    props.getProperty().add(testName);

    Property testCase = new Property();
    testCase.setKey(UnitTestActivity.TEST_CASE);
    testCase.setValue("TestBar");
    props.getProperty().add(testCase);

    Property testCount = new Property();
    testCount.setKey(UnitTestActivity.TEST_COUNT);
    testCount.setValue("1");
    props.getProperty().add(testCount);

    Property testIndice = new Property();
    testIndice.setKey(UnitTestActivity.TEST_INDICE);
    testIndice.setValue("1");
    props.getProperty().add(testIndice);
    
    return sensorData;
  }

  /**
   * Creates SensorData DevEvents for testing.. 
   * 
   * @return List of sensor data entries. 
   * @throws Exception If problems occur. 
   */
  private List<SensorData> makeDevEvents() throws Exception {
    List<SensorData> sensorDataList = new ArrayList<SensorData>();
    
    // Test first
    XMLGregorianCalendar tstamp1 = Tstamp.makeTimestamp("2007-12-18T08:30:45");
    sensorDataList.add(this.makeProdEdit(tstamp1, "210", "30", "2"));

    XMLGregorianCalendar tstamp2 = Tstamp.makeTimestamp("2007-12-18T08:35:41");
    sensorDataList.add(this.makeProdEdit(tstamp2, "309", "45", "2"));
    
    XMLGregorianCalendar tstamp3 = Tstamp.makeTimestamp("2007-12-18T08:38:00");
    sensorDataList.add(this.makeUnitTestEdit(tstamp3, "120", "23", "2", "1", "5"));

    XMLGregorianCalendar tstamp4 = Tstamp.makeTimestamp("2007-12-18T08:40:05");
    sensorDataList.add(this.makeUnitTestEdit(tstamp4, "125", "27", "2", "1", "5"));
    
    XMLGregorianCalendar tstamp5 = Tstamp.makeTimestamp("2007-12-18T08:40:10");
    sensorDataList.add(this.makeUnitTest(tstamp5, "Can not find the data for testing"));

    XMLGregorianCalendar tstamp6 = Tstamp.makeTimestamp("2007-12-18T08:44:05");
    sensorDataList.add(this.makeUnitTestEdit(tstamp6, "180", "50", "2", "1", "5"));

    XMLGregorianCalendar tstamp7 = Tstamp.makeTimestamp("2007-12-18T08:48:00");
    sensorDataList.add(this.makeUnitTest(tstamp7, ""));
    
    // Refactoring
    XMLGregorianCalendar tstamp8 = Tstamp.makeTimestamp("2007-12-18T09:02:49");
    sensorDataList.add(this.makeProdEdit(tstamp8, "501", "99", "4"));

    XMLGregorianCalendar tstamp9 = Tstamp.makeTimestamp("2007-12-18T09:05:00");
    sensorDataList.add(this.makeUnitTest(tstamp9, ""));
    
    return sensorDataList;
  }

  
  /**
   * Test the DevStream processor.
   * 
   * @throws Exception If error occurs.
   */
  @Test public void testProcessor() throws Exception {
    // Defines a processor
    DevStreamBuilder builder = new DevStreamBuilder("TestPass", "tdd", this.logger);
    assertNotNull("Test builder constructor", builder);
    
    List<SensorData> devEvents = makeDevEvents();
    for (SensorData sensorData : devEvents) {
      builder.addDevEvent(sensorData);
    }
    
    DevStream devStream = builder.build();
    assertNotNull("Test DevStream resource", devStream);
    
    List<EpisodeTO> episodeList = devStream.getEpisodeTO();
    assertNotNull("Test episodes in the DevStream resource", episodeList);
    assertEquals("Test number of episodes", 2, episodeList.size());
  }
}